//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  The basic module for MapWindow version 6.0
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either expressed or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is from MapWindow.dll version 6.0
//
// The Initial Developer of this Original Code is Ted Dunsford. Created 6/9/2008 11:39:21 AM
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;


using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;


namespace MapWindow.DirectX
{


    /// <summary>
    /// IndexBuffer
    /// </summary>
    public class IndexCollection : IDrawingComponent
    {
        #region Events

        /// <summary>
        /// Occurs after the collection has been initialized
        /// </summary>
        public event EventHandler Initialized;

        /// <summary>
        /// Occurs when a change makes it necessary to update the cache.
        /// </summary>
        public event EventHandler Invalidated;

        #endregion

        #region Private Variables

        private IndexBuffer _indexBuffer;
        private bool _isInitialized;
        private List<int> _indices;
        private Device _device;
        private int _startIndex;
        private int _bufferOffset = 0;



        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of an Index Collection
        /// </summary>
        public IndexCollection()
        {
            _indices = new List<int>();
        }

        /// <summary>
        /// This makes the assumption that four vertices will come together to form 2 triangles, and that
        /// each four vertex values in the sequence register exactly 2 triangles.
        /// </summary>
        /// <param name="RectangleCount">The number of rectangles that will make up this index set</param>
        public IndexCollection(int RectangleCount)
        {
            // Set up an index buffer.  This will only be required if they set the width or texture, but once it is created, it doesn't have to change.
     


            // Each block is created from 2 triangles.  Each block actually requires 6 indeces, but only 4 vertices.
            // If the upper right or lower left values are no-data, then only one of the triangles is created.
            //
            //    1    4 --- 5        Vertex arrangment:  2  3
            //     |\   \    |                            0  1
            //     | \   \   | 
            //     |  \   \  |       
            //    3 --- 2   6     
            //
            
            _indices = new List<int>();
          
            for(int pt = 0; pt < RectangleCount; pt++)
            {
                // Be sure to skip odd vertices or else the lines try to connect unrelated lines
                
                _indices.Add(pt * 4 + 2);
                _indices.Add(pt * 4 + 1);
                _indices.Add(pt * 4 + 0);
                _indices.Add(pt * 4 + 2);
                _indices.Add(pt * 4 + 3);
                _indices.Add(pt * 4 + 1);
              
            }
           
        }

        /// <summary>
        /// This assumes we have a rectangular grid, so to prevent confusion, assumes the vertices are 
        /// added in raster scan sequence.  It assumes that the vertices match up with edges, not centers,
        /// so it will assume one more vertex in each row than numColumns.
        /// </summary>
        /// <param name="numRows">The number of rows in the raster</param>
        /// <param name="numColumns">The number of cells in each row</param>
        /// <param name="mode">Specifies whether the vertex values are the centers of cells or the edges.</param>
        public IndexCollection(int numRows, int numColumns, VertexModes mode)
        {

            // Each block is created from 2 triangles.  Each block actually requires 6 indeces, but only 4 vertices.
            // If the upper right or lower left values are no-data, then only one of the triangles is created.
            //
            //    1    4 --- 5        Vertex arrangment:  0    1    2    ...
            //     |\   \    |                            N+1  N+2  N+3  ...
            //     | \   \   |                            2N+1 2N+2 2N+3 ...
            //     |  \   \  |                            ...  ...  ...  ...
            //    3 --- 2   6     
            //
            _indices = new List<int>();
            int vertexRows = numRows;
            int vertexColumns = numColumns;
            if (mode == VertexModes.Bilinear)
            {
                vertexRows = numRows + 1;
                vertexColumns = numColumns + 1;
            }
            for (int row = 0; row < vertexRows-1; row++)
            {
                for (int col = 0; col < vertexColumns-1; col++)
                {
                    _indices.Add(row * vertexColumns + col);
                    _indices.Add((row + 1) * vertexColumns + col + 1);
                    _indices.Add((row + 1) * vertexColumns + col);
                    _indices.Add(row * vertexColumns + col);
                    _indices.Add(row * vertexColumns + col + 1);
                    _indices.Add((row + 1) * vertexColumns + col + 1);
                }
            }
        }


        /// <summary>
        /// Creates a new instance of an Index Collection using the specified indices
        /// </summary>
        /// <param name="indexValues"></param>
        public IndexCollection(List<int> indexValues)
        {
            _indices = indexValues;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Adds a single index value ot the list
        /// </summary>
        /// <param name="index">The integer index to add</param>
        public void Add(int index)
        {
            _indices.Add(index);
        }

        /// <summary>
        /// This doesn't physically draw the indicies, but simply sets the index buffer.
        /// If the index buffer hasn't yet been initialized, this will take care of that.
        /// </summary>
        /// <param name="device"></param>
        public virtual void BeforeDrawing(Device device)
        {
            _device = device;
            if (_isInitialized == false)
            {
                Initialize();
            }
            _device.Indices = _indexBuffer;
        }

        /// <summary>
        /// This method should be called after the drawing has taken place.
        /// </summary>
        /// <param name="device">The Microsoft.DirectX.Direct3D.Device</param>
        public virtual void AfterDrawing(Device device)
        {
            _device.Indices = null;
        }

        /// <summary>
        /// Initializes the vertices and indices
        /// </summary>
        protected virtual void Initialize()
        {
            if (_indexBuffer != null) _indexBuffer.Dispose();
            _indexBuffer = new IndexBuffer(typeof(int), _indices.Count, _device, Usage.WriteOnly, Pool.Managed);
            _indexBuffer.Created += new EventHandler(_indexBuffer_Created);
            OnInitialized(this, new EventArgs());
        }

        void _indexBuffer_Created(object sender, EventArgs e)
        {
            OnInitialized(this, new EventArgs());
        }

        /// <summary>
        /// Disposes the index buffer.
        /// </summary>
        public virtual void Dispose()
        {
            if(_indexBuffer != null)_indexBuffer.Dispose();
        }

        /// <summary>
        /// This simply instructs the index buffer to be re-generated the next drawing cycle.
        /// This does not dispose any unmanaged buffers.  The will automatically be disposed
        /// immediately before creating a new one.
        /// </summary>
        public virtual void Invalidate()
        {
            _isInitialized = false;
        }


        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the position of the first index in the unmanaged buffer.
        /// The buffer might, for instance, be larger than what is represented by this collection.
        /// This should usually be 0.
        /// </summary>
        public virtual int BufferOffset
        {
            get { return _bufferOffset; }
            set { _bufferOffset = value; }
        }

        /// <summary>
        /// Gets the count
        /// </summary>
        public virtual int Count
        {
            get { return _indices.Count; }
        }

        /// <summary>
        /// Is Initialized
        /// </summary>
        public virtual bool IsInitialized
        {
            get { return _isInitialized; }
            set { _isInitialized = value; }
        }

        /// <summary>
        /// Gets or sets the zero-based integer starting index for this list.
        /// </summary>
        public virtual int StartIndex
        {
            get { return _startIndex; }
            set 
            {
                if (value < 0)
                {
                    _startIndex = 0;
                    return;
                }
                if (value >= _indices.Count)
                {
                    throw new ArgumentOutOfRangeException("The start index specified was larger than the number of indices.");
                }
                _startIndex = value;
            }
        }


        #endregion

        #region Protected Methods

        /// <summary>
        /// Raises the Initialized event
        /// </summary>
        protected virtual void OnInitialized(object sender, EventArgs e)
        {
            _indexBuffer.SetData(_indices.ToArray(), 0, LockFlags.None);
            if (Initialized != null)
            {
                Initialized(this, new EventArgs());
            }
        }

        /// <summary>
        /// Raises the Invalidated method using the sender and EventArgs specified.
        /// </summary>
        /// <param name="sender">The object sender</param>
        /// <param name="e">The System.EventArgs</param>
        protected virtual void OnInvalidated(object sender, EventArgs e)
        {
            if (Invalidated != null)
            {
                Invalidated(sender, e);
            }
        }

        
        
        #endregion



    }
}
